#nullable enable

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PHMEE.CommonUtils;
using PHMEE.TDengineInfrastructure;

namespace PHMEE.EnergyEfficiency;

public sealed class GpsTdRepository : BaseTdRepository, IGpsTdRepository
{
    /// <summary>
    /// Gps 位于能效库
    /// </summary>
    private const DbPrefixEnum DbPrefix = DbPrefixEnum.Ee;

    /// <summary>
    /// Gps 约定好的超级表名
    /// </summary>
    private const string SuperTableName = "gps";

    /// <summary>
    /// Gps 约定好的子表名
    /// </summary>
    private const string SubTableName = "gps_001";
    
    private readonly ILogger<GpsTdRepository> _logger;

    public GpsTdRepository(IOptionsSnapshot<TDengineOptions> tdengineOptions, ILogger<GpsTdRepository> logger) : base(tdengineOptions)
    {
        _logger = logger;
    }

    public async Task<GpsTde?> LatestOrDefaultAsync(Guid vesselId, DateTime startDateTimeUtc, DateTime endDateTimeUtc)
    {
        try
        {
            string dbName = PhmeeConst.GetDbName(DbPrefix, vesselId);

            await using var command = TDConnection.CreateCommand();

            command.CommandText =
                $"select ts, latitude, longitude, ground_speed, distance from {dbName}.{SubTableName} where ts >= '{startDateTimeUtc.ToFormattedUtcString()}' and ts <= '{endDateTimeUtc.ToFormattedUtcString()}' order by ts desc limit 1";

            await using var reader = await command.ExecuteReaderAsync();

            if (await reader.ReadAsync())
            {
                return new GpsTde
                {
                    Ts = GetDateTimeUtc("ts", reader),
                    Latitude = reader.GetValue(reader.GetOrdinal("latitude")) as float? ?? default,
                    Longitude = reader.GetValue(reader.GetOrdinal("longitude")) as float? ?? default,
                    GroundSpeed = reader.GetValue(reader.GetOrdinal("ground_speed")) as float? ?? default,
                    Distance = reader.GetValue(reader.GetOrdinal("distance")) as float? ?? default
                };
            }

            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"TDengine 仓储: {nameof(GpsTdRepository)}.{nameof(LatestOrDefaultAsync)} 失败");
            throw new TDengineRepositoryException(vesselId, null, startDateTimeUtc, endDateTimeUtc, "根据时间范围查询 Gps 数据失败", ex);
        }
    }

    public async Task<GpsTde?> LatestOrDefaultAsync(Guid vesselId, DateTime dateTimeUtc)
    {
        try
        {
            string dbName = PhmeeConst.GetDbName(DbPrefix, vesselId);

            await using var command = TDConnection.CreateCommand();

            command.CommandText =
                $"select ts, latitude, longitude, ground_speed, distance from {dbName}.{SubTableName} where ts <= '{dateTimeUtc.ToFormattedUtcString()}' order by ts desc limit 1";

            await using var reader = await command.ExecuteReaderAsync();

            if (await reader.ReadAsync())
            {
                return new GpsTde
                {
                    Ts = GetDateTimeUtc("ts", reader),
                    Latitude = reader.GetValue(reader.GetOrdinal("latitude")) as float? ?? default,
                    Longitude = reader.GetValue(reader.GetOrdinal("longitude")) as float? ?? default,
                    GroundSpeed = reader.GetValue(reader.GetOrdinal("ground_speed")) as float? ?? default,
                    Distance = reader.GetValue(reader.GetOrdinal("distance")) as float? ?? default
                };
            }

            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"TDengine 仓储: {nameof(GpsTdRepository)}.{nameof(LatestOrDefaultAsync)} 失败");
            throw new TDengineRepositoryException(vesselId, null, dateTimeUtc, null, "根据时间查询 Gps 数据失败", ex);
        }
    }

    /// <summary>
    /// 查询子表是否存在
    /// </summary>
    /// <param name="vesselId"></param>
    /// <returns></returns>
    public async Task<bool> IsSubTableExistAsync(Guid vesselId)
    {
        try
        {
            string dbName = PhmeeConst.GetDbName(DbPrefix, vesselId);

            await using var command = TDConnection.CreateCommand();

            command.CommandText = $"select distinct tbname from {dbName}.{SuperTableName} where tbname = '{SubTableName}';";

            await using var reader = await command.ExecuteReaderAsync();

            if (await reader.ReadAsync())
            {
                var tbname = reader.GetString(reader.GetOrdinal("tbname"));
                return tbname == SubTableName;
            }

            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"TDengine 仓储: {nameof(GpsTdRepository)}.{nameof(IsSubTableExistAsync)} 失败");
            throw new TDengineRepositoryException(vesselId, null, null, null, "查询 Gps 子表是否存在失败", ex);
        }
    }

    public async Task<List<GpsTde>> GetListCloseOpenAscAsync(Guid vesselId, DateTime startDateTimeUtc, DateTime endDateTimeUtc)
    {
        try
        {
            string dbName = PhmeeConst.GetDbName(DbPrefix, vesselId);

            await using var command = TDConnection.CreateCommand();

            command.CommandText =
                $"select ts, latitude, longitude, ground_speed, distance from {dbName}.{SubTableName} where ts >= '{startDateTimeUtc.ToFormattedUtcString()}' and ts < '{endDateTimeUtc.ToFormattedUtcString()}' order by ts asc";

            await using var reader = await command.ExecuteReaderAsync();

            var list = new List<GpsTde>();

            while (await reader.ReadAsync())
            {
                list.Add(new GpsTde
                {
                    Ts = GetDateTimeUtc("ts", reader),
                    Latitude = reader.GetValue(reader.GetOrdinal("latitude")) as float? ?? default,
                    Longitude = reader.GetValue(reader.GetOrdinal("longitude")) as float? ?? default,
                    GroundSpeed = reader.GetValue(reader.GetOrdinal("ground_speed")) as float? ?? default,
                    Distance = reader.GetValue(reader.GetOrdinal("distance")) as float? ?? default
                });
            }

            return list;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"TDengine 仓储: {nameof(GpsTdRepository)}.{nameof(GetListCloseOpenAscAsync)} 失败");
            throw new TDengineRepositoryException(vesselId, null, startDateTimeUtc, endDateTimeUtc, "根据时间范围 close open 查询 Gps 数据失败", ex);
        }
    }

    /// <summary>
    /// 查询航程列表
    /// </summary>
    /// <param name="vesselId"></param>
    /// <param name="startDateTimeUtc"></param>
    /// <param name="endDateTimeUtc"></param>
    /// <returns></returns>
    /// <exception cref="TDengineRepositoryException"></exception>
    public async Task<List<float>> GetDistancesCloseOpenAsync(Guid vesselId, DateTime startDateTimeUtc, DateTime endDateTimeUtc)
    {
        try
        {
            string dbName = PhmeeConst.GetDbName(DbPrefix, vesselId);

            await using var command = TDConnection.CreateCommand();

            command.CommandText =
                $"select distance from {dbName}.{SubTableName} where ts >= '{startDateTimeUtc.ToFormattedUtcString()}' and ts < '{endDateTimeUtc.ToFormattedUtcString()}' order by ts asc";

            await using var reader = await command.ExecuteReaderAsync();

            var list = new List<float>();

            while (await reader.ReadAsync())
            {
                list.Add(reader.GetValue(reader.GetOrdinal("distance")) as float? ?? default);
            }

            return list;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"TDengine 仓储: {nameof(GpsTdRepository)}.{nameof(GetDistancesCloseOpenAsync)} 失败");
            throw new TDengineRepositoryException(vesselId, null, startDateTimeUtc, endDateTimeUtc, "根据时间范围 close open 查询 Gps distance 列表失败", ex);
        }
    }
}